<?php
/**
 * Class used to build (clean) query for solr. 
 * Inspîred by sfLuceneCritera from sfLucenePlugin
 * Usage example: 
 * <code>
 *   $c = uvmcSolrQuery::newInstance()->andTerm('the cool dude')->andField('component', array('Forum', 'Blog'));
 * </code>
 *
 *
 * @package    uvmcSolrSearchPlugin
 * @subpackage Query
 * @author     Marc Weistroff <mweistroff@uneviemoinschere.com>
 * @version    $Id: uvmcSolrQuery.class.php 10 2009-10-22 13:46:49Z gterrain $
 *
 */

class uvmcSolrQuery
{
  const 
    OPERATOR_AND = 'AND',
    OPERATOR_OR  = 'OR';
  
  protected 
    $query = null;
    
  public function __construct()
  {
    $this->query = '';
  }
  
  /**
   * add a term with AND operator
   * @param $term string
   * @return $this
   */
  public function andTerm($term)
  {
    return $this->addTerm($term, self::OPERATOR_AND);
  }
  
  /**
   * add a term with OR operator
   * @param $term string
   */
  public function orTerm($term)
  {
    return $this->addTerm($term, self::OPERATOR_OR);
  }
  
  /**
   * add a term to the query
   * @param $term string
   * @param $operator const/string
   * @return $this
   */
  public function addTerm($term, $operator)
  {
    if(empty($this->query))
    {
      $this->query = $term;
    }
    else
    {
      $this->query .= ' '.$operator.' '.$term;
    }
    
    return $this;
  }
  
  /**
   * 
   */
  public function addField($field, $value, $operator, $inside_operator = self::OPERATOR_OR)
  {
    if(is_array($value) && count($value) == 1)
    {
      $value = $value[0];
    }
    
    $this->query .= empty($this->query) ? '' : ' '.$operator.' ';
    $this->query .= '(';
    if (is_array($value))
    {
      $i = 0;
      $subs = array();
      $maxCount = count($value);
      foreach($value as $v)
      {
        $subs[] .= '('.$field.':'.self::sanitize($v).')';
      }
      $this->query .= implode(' '.$inside_operator.' ', $subs);
    }
    else
    {
      $this->query .= $field.':'.self::sanitize($value);
    }
    $this->query .= ')';
    
    return $this;
  }
  
  /**
   * 
   */
  public function andField($field, $value, $inside_operator = self::OPERATOR_OR)
  {
    $this->addField($field, $value, self::OPERATOR_AND, $inside_operator);
    return $this;
  }
  
  /**
   * 
   */
  public function orField($field, $value, $inside_operator = self::OPERATOR_OR)
  {
    $this->addField($field, $value, self::OPERATOR_OR, $inside_operator);
    return $this;
  }
  
  /**
   * Simply provides a way to do one line method chaining
   * 
   * @return uvmcSolrQuery
   */
  static public function create()
  {
    return new self();
  }

  /**
   * Adds a subquery to the query itself.  It accepts either a string which will
   * be parsed or a Zend query.
   * 
   * @return uvmcSolrQuery
   */
  public function add($query, $type = uvmcSolrQuery::OPERATOR_AND)
  {

    if(strlen($this->query) != 0)
    {
      $this->query .= ' '. $type;
    }
    
    if($query instanceof uvmcSolrQuery)
    {
      $this->query .=  ' ('.$query->getQueryString().')';
    }
    else
    {
      $this->query .= ' '.$query;
    }
    
    return $this;
  }
  
  public function addString($query, $type = uvmcSolrQuery::OPERATOR_AND)
  {
    return $this->add($query, $type);
  }

  /**
   * Adds a range subquery
   * 
   * @return uvmcSolrQuery
   */
  public function addRange($start = null, $stop = null, $field = null, $inclusive = true, $type = uvmcSolrQuery::OPERATOR_AND)
  {
    
    if ($stop == null && $start == null)
    {
      throw new sfException('You must specify at least a start or stop in a range query.');
    }

    if($inclusive)
    {
      $query = $field . ':['.$start.' TO '.$stop.']';
    }
    else
    {
      $query = $field . ':{'.$start.' TO '.$stop.'}';
    }
    
    return $this->add($query, $type);
  }

  /**
   * 
   * @return uvmcSolrQuery
   */
  public function getNewQuery()
  {
    return new self();
  }
  
  public function getQueryString()
  {
    return $this->query;
  }
  
  public function reset()
  {
    $this->query = "";
    return $this;
  }
  
  public static function sanitize($keyword)
  {
    $keyword = trim($keyword);
    if(strlen($keyword) == 0)
    {
      return false;
    }
    
    $keyword = str_replace('"', '\"', $keyword);

    return '"'.$keyword.'"';
  }
  
  public static function removeStartingWildcards($keyword)
  {
    $keyword = trim($keyword);
    $forbidden = array('?', '*');
    $j = strlen($keyword);
    for ($i = 0; $i < $j; $i++) {
      if (!in_array($keyword{$i}, $forbidden)) {
        return substr($keyword, $i);
      }
    }
    return '';
  }
}